{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial: Recovering an AES key by Differential Fault Analysis attack\n",
    "\n",
    "Supported setups:\n",
    "\n",
    "SCOPES:\n",
    "\n",
    "* OPENADC\n",
    "\n",
    "PLATFORMS:\n",
    "\n",
    "* CWLITEARM\n",
    "* CWLITEXMEGA\n",
    "\n",
    "This tutorial will introduce you to the Differential Fault Analysis (DFA) attack. It will show you how to configure the  target to perform AES encryptions, how to glitch the encryption operations and introduce errors in the computed ciphertext and finally how to process these glitched ciphertexts to extract the AES key. This can be used as how-to for attacking other targets as well.\n",
    "\n",
    "Original author: Philippe Teuwen ([\\@doegox](https://twitter.com/doegox))\n",
    "\n",
    "License: CC-BY-SA  \n",
    "\n",
    "Improvements are welcome!\n",
    "\n",
    "> **Thanks to Philippe Teuwen for contributing this tutorial, he was the winner of the ChipWhisperer 2018 Contest with this submission!**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "SCOPETYPE = \"OPENADC\"\n",
    "PLATFORM = \"CWLITEARM\"\n",
    "CRYPTO_TARGET = \"TINYAES128C\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Prerequisites"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### On giants' shoulders\n",
    "This tutorial is the first to deal with DFA, nevertheless it was not designed from scratch.\n",
    "It relies on multiple sources we strongly encourage your to read as well for a better understanding of the details.\n",
    "\n",
    "* The `simpleserial-aes` target firmware, which contains the software AES implementation used in other side-channel analysis tutorials such as [Using CW Analyzer for CPA Attack](PA_CPA_1-Using_CW-Analyzer_for_CPA_Attack.ipynb). This is the implementation we will attack.\n",
    "* The glitching tutorials:\n",
    "  * [Introduction to Glitch Attacks](Fault_1-Introduction_to_Clock_Glitch_Attacks.ipynb), useful to understand the hardware implementation of the glitching module\n",
    "  * [Tutorial CW305-3 Clock Glitching (wiki)](https://wiki.newae.com/Tutorial_CW305-3_Clock_Glitching) which also presented briefly the principles of glitching AES, but without exploiting the faults\n",
    "* The DFA attack itself: there is no DFA cryptanalysis code in the Chipwhisperer but we'll re-use a Python library the author of this tutorial wrote for attacking white-box implementations. It's called `phoenixAES` and it is available on [Github](https://github.com/SideChannelMarvels/JeanGrey) and on PyPI."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Brief introduction to Differential Fault Analysis\n",
    "We'll only describe the general principle and the operational constraints.\n",
    "\n",
    "The principle is to repeat the same AES operation over and over and to glitch its intermediate operations to get an output cryptographically incorrect. There are many DFA algorithms which, depending on the nature of the fault (single bit?, single byte?, how many faulted outputs can we collect?...), are able to recover the last round key of the AES with various computations that may be quite intensive.\n",
    "\n",
    "This tutorial covers the recovering of the key of a simple AES-128 encryption.\n",
    "\n",
    "We'll use a quite simple DFA published initially by Dusart, Letourneux and Vivolo in 2002 which has nice properties. For a mathematical deep-dive of the DFA we're using in this tutorial, you can read [Differential Fault Analysis on White-box AES Implementations](https://blog.quarkslab.com/differential-fault-analysis-on-white-box-aes-implementations.html) as we will use the exact same DFA library. Moreover, the blog post explains how to tackle DFA against AES decryption and how to attack more than one round, which is required to attack AES-192 or AES-256.\n",
    "\n",
    "AES-128 is made of 10 rounds, the last one is missing the *MixColumn* operation, the only operation which brings diffusion, i.e. it's an operation which makes a single byte of one round state affecting multiple bytes in the next round, 4 bytes to be exact.\n",
    "\n",
    "![aes_operations.png](img/aes_operations.png)\n",
    "\n",
    "(source: http://www.iis.ee.ethz.ch/~kgf/acacia/fig/aes.png)\n",
    "\n",
    "So, if we inject a fault which affects a single byte between the last two *MixColumn* operations, it will propagate and 4 of the 16 output bytes will be wrong. We don't need to know precisely where we inject our faults, we can simply observe the output and look for a 4-byte fault with one of the 4 possible patterns. The attack is *differential* because we observe the difference between the correct output and the faulty outputs.\n",
    "We'll save you the maths but with 2 such faults on the same column, there is a high probability to recover a quarter of the round key, so with 4\\*2 faults we can recover the entire round key. And because the AES keyschedule is invertible, we can compute it backwards and recover the first round key, which is by definition equal to the AES-128 key.\n",
    "\n",
    "So, our operational constraints are quite simple: be able to run several times the same AES encryption, with the same key (doh!) and the same plaintext input and be able to collect the ciphertexts. Note that technically we don't need to know the value of the plaintext, we only need it to be constant."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Installing dependencies\n",
    "\n",
    "Firstly, let's install `phoenixAES` in the current kernel environment:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "!{sys.executable} -m pip install phoenixAES"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Target\n",
    "\n",
    "### Which target?\n",
    "\n",
    "Let's recap. This tutorial is specifically focusing on:\n",
    "* AES-128 encryption\n",
    "* the Chipwhisperer Lite ARM or XMEGA target\n",
    "* AVR Crypto Lib or TinyAES128C\n",
    "* clock glitching\n",
    "\n",
    "Other targets and AES implementations should be equally working as well as power glitching. Obviously the glitching parameters will have to be adapted to the corresponding target, which is often not that straightforward.  \n",
    "\n",
    "Even if you run this tutorial on the same Chipwhisperer Lite target hardware, you might have to alter slightly the glitching parameters to be able to get working glitches. Glitching is so sensitive that running twice the exact same attack hardly produce the exact same results."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Building the target firmware\n",
    "\n",
    "If you have the `avr-gcc` toolchain installed, you should be able to build the `simpleserial-aes-CW303` firmware:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash -s \"$PLATFORM\" \"$CRYPTO_TARGET\"\n",
    "cd ../hardware/victims/firmware/simpleserial-aes\n",
    "make PLATFORM=$1 CRYPTO_TARGET=$2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Attack setup"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### CW-lite connection and target flashing\n",
    "\n",
    "Connect to the Chipwhisperer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run \"Helper_Scripts/Setup_Generic.ipynb\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Flash the target:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fw_path = \"../hardware/victims/firmware/simpleserial-aes/simpleserial-aes-{}.hex\".format(PLATFORM)\n",
    "cw.program_target(scope, prog, fw_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### First execution\n",
    "\n",
    "For the DFA attack, we need a constant plaintext (and constant key of course).\n",
    "We could just use two bytearrays but let's use the CW API to demonstrate its usage.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ktp = cw.ktp.Basic()\n",
    "ktp.fixed_text = True\n",
    "ktp.fixed_key = True\n",
    "key, text = ktp.next()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Assuming we want to record traces, let's capture the entire AES.  \n",
    "It's useful to see which round(s) we'll glitch by tuning `scope.glitch.ext_offset` later."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.clock.adc_src = \"clkgen_x1\"\n",
    "if PLATFORM == \"CWLITEXMEGA\" or PLATFORM == \"CW303\":\n",
    "    scope.adc.samples = 20000\n",
    "elif PLATFORM == \"CWLITEARM\" or PLATFORM == \"CW308_STM32F3\":\n",
    "    scope.adc.samples = 8000"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's test our setup with a first execution, without fault.\n",
    "It will give us the golden reference output."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# make sure glitches are disabled (in case cells are re-run)\n",
    "scope.io.hs2 = \"clkgen\"\n",
    "\n",
    "trace = cw.capture_trace(scope, target, text, key)\n",
    "goldciph = trace.textout\n",
    "print(\"Plaintext: {}\".format(text.hex()))\n",
    "print(\"Key:       {}\".format(key.hex()))\n",
    "print(\"Ciphertext:{}\".format(goldciph.hex()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "reset_target(scope)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Just to be sure, let's check..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from Crypto.Cipher import AES\n",
    "aes = AES.new(bytes(key), AES.MODE_ECB)\n",
    "goldciph2 = aes.encrypt(bytes(text))\n",
    "print(\"Expected ciphertext:  {}\".format(goldciph2.hex()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's draw the full AES execution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import holoviews as hv\n",
    "from holoviews import opts\n",
    "hv.extension('bokeh')\n",
    "curve = hv.Curve(trace.wave).opts(width=600, height=600)\n",
    "\n",
    "# add boxes around last rounds\n",
    "\n",
    "if PLATFORM == \"CWLITEXMEGA\" or PLATFORM == \"CW303\":\n",
    "    line = hv.Path([(11600, -0.25), (11600, 0.25), (13200, 0.25), (13200, -0.25), (11600, -0.25)], label='8th round').opts(color=\"red\", show_legend=True) * \\\n",
    "            hv.Path([(13250, -0.25), (13250, 0.25), (14850, 0.25), (14850, -0.25), (13250, -0.25)], label='9th round').opts(color=\"green\", show_legend=True) * \\\n",
    "            hv.Path([(14900, -0.25), (14900, 0.25), (16000, 0.25), (16000, -0.25), (14900, -0.25)], label='10th round').opts(color=\"yellow\", show_legend=True)\n",
    "elif PLATFORM == \"CWLITEARM\" or PLATFORM == \"CW308_STM32F3\":\n",
    "    line = hv.Path([(5050, -0.1), (5050, 0.2), (5700, 0.2), (5700, -0.1), (5050, -0.1)], label='8th round').opts(color=\"red\", show_legend=True) * \\\n",
    "            hv.Path([(5750, -0.1), (5750, 0.2), (6400, 0.2), (6400, -0.1), (5750, -0.1)], label='9th round').opts(color=\"green\", show_legend=True) * \\\n",
    "            hv.Path([(6450, -0.1), (6450, 0.2), (7000, 0.2), (7000, -0.1), (6450, -0.1)], label='10th round').opts(color=\"yellow\", show_legend=True)\n",
    "    pass\n",
    "\n",
    "#plt.show()\n",
    "(curve * line).opts(opts.Path(line_width=3)).opts(width=600, height=600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see clearly the 10 AES-128 rounds, the 10th round being smaller than the others as there is no *MixColumn*."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### First glitches"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To check the actual clock glitches with an oscilloscope, you can probe pin 6 of the CW 20 pin connector and run the following function to glitch all clock cycles during 2 seconds. Beware that the probe influences slightly the signal and it's enough to require a different tuning of the glitching parameters, so when you're attacking a target, do it all with or all without the oscilloscope but avoid messing up your setup!\n",
    "In this tutorial, parameters were tuned without attached probe. Still, your board might require slightly different values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "def test_glitches():\n",
    "    if PLATFORM == \"CWLITEXMEGA\" or PLATFORM == \"CW303\":\n",
    "        scope.io.hs2 = \"glitch\"\n",
    "        scope.glitch.clk_src = 'clkgen'\n",
    "        scope.glitch.width=3.5\n",
    "        scope.glitch.offset=34\n",
    "        scope.glitch.trigger_src='continuous'\n",
    "    elif PLATFORM == \"CWLITEARM\" or PLATFORM == \"CW308_STM32F3\":\n",
    "        scope.io.hs2 = \"glitch\"\n",
    "        scope.glitch.clk_src = \"clkgen\"\n",
    "        scope.glitch.width = -10.15625\n",
    "        scope.glitch.offset = -39.84\n",
    "        scope.glitch.trigger_src='continuous'\n",
    "\n",
    "def stop_test_glitches():\n",
    "    scope.glitch.trigger_src='ext_single'\n",
    "\n",
    "test_glitches()\n",
    "time.sleep(2)\n",
    "stop_test_glitches()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is an example of five glitched clock cycles as seen with an oscilloscope:\n",
    "\n",
    "![clock_glitches.png](img/clock_glitches.png)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "See how the actual width and offset values are rounded to the internal step values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(scope.glitch)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's define a `MIN_STEP` equal to the internal step value, it'll be our \"unit\" width and offset step and other values will be rounded to the closest multiple."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "MIN_STEP=25/64"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see the effect of clock glitches on the AES execution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Initial glitch parameters\n",
    "if PLATFORM == \"CWLITEXMEGA\" or PLATFORM == \"CW303\":\n",
    "    scope.io.hs2 = \"glitch\"\n",
    "    scope.glitch.clk_src = 'clkgen'\n",
    "    scope.glitch.width=3.5\n",
    "    scope.glitch.offset=34\n",
    "    scope.glitch.trigger_src='ext_single'\n",
    "    scope.glitch.ext_offset = 13000\n",
    "elif PLATFORM == \"CWLITEARM\" or PLATFORM == \"CW308_STM32F3\":\n",
    "    scope.io.hs2 = \"glitch\"\n",
    "    scope.glitch.clk_src = \"clkgen\"\n",
    "    scope.glitch.width = -10.15625 + 1\n",
    "    scope.glitch.offset = -39.84\n",
    "    scope.glitch.ext_offset = 5400\n",
    "    scope.glitch.repeat = 3\n",
    "    scope.glitch.trigger_src='ext_single'\n",
    "\n",
    "# reset target\n",
    "reset_target(scope)\n",
    "time.sleep(0.1)\n",
    "\n",
    "trace = cw.capture_trace(scope, target, text, key)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "curve = hv.Curve(trace.wave)\n",
    "curve *= hv.Path([(scope.glitch.ext_offset, 0.25), (scope.glitch.ext_offset, -0.3)]).opts(color=\"red\")\n",
    "curve.opts(width=600, height=600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You should see a glitch in the power trace (blue) when the clock was glitched (red dotted line).  \n",
    "As said earlier, glitch parameters may have to be adapted to your specific hardware.  Our experience is that a good `scope.glitch.width` is one just a bit smaller than one producing a clearly visible glitch in the power trace. E.g. the trace above was created with `scope.glitch.width=6*MIN_STEP` and we'll use `scope.glitch.width=5*MIN_STEP` in our attack. If this is not precise enough, consider tuning `scope.glitch.width_fine` too."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Campain setup\n",
    "Now, we'll prepare a campaign of clock glitches to induce faults.\n",
    "\n",
    "The following code is a bit more complex than strictly needed but we want to be able to compute exactly how many executions will be performed depending on the ranges and steps of the variables we want to tune. This allows us to get a nice progress bar.  \n",
    "We'll sample three different axes:\n",
    "* `scope.glitch.width`: clock glitch width\n",
    "* `scope.glitch.offset`: clock glitch offset\n",
    "* `scope.glitch.ext_offset`: offset since the initial trigger (to target the last rounds)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import namedtuple\n",
    "# named tuples to make it easier to change the scope of the test\n",
    "Range = namedtuple('Range', ['min', 'max', 'step'])\n",
    "\n",
    "import math\n",
    "\n",
    "# get control over logging in order to be able to mask target execution errors,\n",
    "# which can easily happen when glitching the target!\n",
    "import logging\n",
    "logging.basicConfig(level=logging.WARN)\n",
    "\n",
    "# Let's be prepared for user-provided ranges: rounding and checking consistency by ourselves.\n",
    "def apply_ranges():\n",
    "    global width_range, width_range_steps\n",
    "    global offset_range, offset_range_steps\n",
    "    global extoffset_range, extoffset_range_steps\n",
    "    width_step_sign = width_range.step/abs(width_range.step)\n",
    "    offset_step_sign = offset_range.step/abs(offset_range.step)\n",
    "    width_range = Range(width_range.min, width_range.max, round(width_range.step / MIN_STEP) * MIN_STEP)\n",
    "    offset_range = Range(offset_range.min, offset_range.max, round(offset_range.step / MIN_STEP) * MIN_STEP)\n",
    "    if abs(width_range.step) < MIN_STEP:\n",
    "        step = width_step_sign*MIN_STEP\n",
    "        logging.error('width_range.step too small, adjusting to {}'.format(step))\n",
    "        width_range = Range(width_range.min, width_range.max, step)\n",
    "    if abs(offset_range.step) < MIN_STEP:\n",
    "        step = offset_step_sign*MIN_STEP\n",
    "        logging.error('offset_range.step too small, adjusting to {}'.format(step))\n",
    "        offset_range = Range(offset_range.min, offset_range.max, step)\n",
    "    width_range_steps = math.ceil((width_range.max-width_range.min)/width_range.step)\n",
    "    offset_range_steps = math.ceil((offset_range.max-offset_range.min)/offset_range.step)\n",
    "    extoffset_range_steps = math.ceil((extoffset_range.max-extoffset_range.min)/extoffset_range.step)\n",
    "    if width_range_steps < 0:\n",
    "        step = -width_range.step\n",
    "        logging.error('width_range.step has wrong sign, adjusting to {}'.format(step))\n",
    "        width_range = Range(width_range.min, width_range.max, step)\n",
    "        width_range_steps = -width_range_steps\n",
    "    if offset_range_steps < 0:\n",
    "        step = -offset_range.step\n",
    "        logging.error('offset_range.step has wrong sign, adjusting to {}'.format(step))\n",
    "        offset_range = Range(offset_range.min, offset_range.max, step)\n",
    "        offset_range_steps = -offset_range_steps\n",
    "    if extoffset_range_steps < 0:\n",
    "        step = -extoffset_range.step\n",
    "        logging.error('extoffset_range.step has wrong sign, adjusting to {}'.format(step))\n",
    "        extoffset_range = Range(extoffset_range.min, extoffset_range.max, step)\n",
    "        extoffset_range_steps = -extoffset_range_steps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is not strictly required for the tutorial but here are few global variables that you can tune to decide if, besides the DFA attack, you want also to:\n",
    "* `GLITCH_RESULTS_FILEPATH`: record the ciphertexts in a CSV file (string or None)\n",
    "* `TRACES_FILEPATH`: record the consumption traces in a Numpy file (string or None)\n",
    "\n",
    "The goal is to demonstrate various parts of the CW API that might help you debugging real-life DFA campains."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "GLITCH_RESULTS_FILEPATH='/tmp/glitch_outputs.csv'\n",
    "TRACES_FILEPATH='/tmp/glitch_traces.npy'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "GLITCH_RESULTS_FILEPATH=None\n",
    "TRACES_FILEPATH=None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next cell defines the glitches campain.    \n",
    "`traces` is the list of recorded traces, `output` the list of outputs, either errors (e.g. if the target crashed) or (faulty or correct) ciphertexts. To be able to display a table of the glitch results and the faulty ciphertexts, we'll store the interesting information in the list `results`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def campaign():\n",
    "    import time\n",
    "    global traces, outputs, results\n",
    "    traces = []\n",
    "    outputs = []\n",
    "    results = [['#', 'target output', 'width', 'offset', 'extoffset', 'interesting']]\n",
    "\n",
    "    # Initial glitch parameters\n",
    "    scope.io.hs2 = \"glitch\"\n",
    "    scope.glitch.clk_src = 'clkgen'\n",
    "    scope.glitch.trigger_src = 'ext_single'\n",
    "    scope.glitch.repeat = glitch_repeat\n",
    "    scope.glitch.width = width_range.min\n",
    "    scope.glitch.offset = offset_range.min\n",
    "    scope.glitch.ext_offset = extoffset_range.min\n",
    "\n",
    "    if GLITCH_RESULTS_FILEPATH is not None:\n",
    "        import csv\n",
    "        f = open(GLITCH_RESULTS_FILEPATH, 'w')\n",
    "        writer = csv.writer(f)\n",
    "\n",
    "    # campain loop with progress bar\n",
    "    from tqdm import tnrange, tqdm\n",
    "    for i in tnrange(width_range_steps*offset_range_steps*extoffset_range_steps, desc='Capturing traces', file=sys.stdout):\n",
    "\n",
    "        # reset target\n",
    "        reset_target(scope)\n",
    "\n",
    "        # not very useful in this case as we're using fixed key & text, but this demonstrates the API.\n",
    "        key, text = ktp.next()\n",
    "        logging.getLogger().setLevel(logging.ERROR)\n",
    "\n",
    "        trace = cw.capture_trace(scope, target, text, key)\n",
    "        if trace:\n",
    "            # shall we acquire the trace?\n",
    "            if TRACES_FILEPATH is not None:\n",
    "                traces.append(trace.wave)\n",
    "\n",
    "        # read target output from the target's buffer\n",
    "        # we know it can fail, so let's silent warnings for now\n",
    "        \n",
    "        output = trace.textout\n",
    "        logging.getLogger().setLevel(logging.WARN)\n",
    "\n",
    "        # at this stage, we consider any 32b output different from the reference as potentially interesting\n",
    "        interesting = output is not None and len(output) == 16 and output != goldciph\n",
    "\n",
    "        # let's record it\n",
    "        if output is not None and len(output) == 16:\n",
    "            r = bytes(output).hex()\n",
    "        else:\n",
    "            r = repr(output)\n",
    "        data = [i, r, scope.glitch.width, scope.glitch.offset, scope.glitch.ext_offset, interesting]\n",
    "        results.append(data)\n",
    "\n",
    "        if GLITCH_RESULTS_FILEPATH is not None:\n",
    "            writer.writerow(data)\n",
    "        if interesting:\n",
    "            outputs.append(output)\n",
    "\n",
    "        # loop update: compute next set of parameters\n",
    "        scope.glitch.ext_offset += extoffset_range.step\n",
    "        if scope.glitch.ext_offset >= extoffset_range.max:\n",
    "            scope.glitch.ext_offset = extoffset_range.min\n",
    "            scope.glitch.offset += offset_range.step\n",
    "            if scope.glitch.offset >= offset_range.max:\n",
    "                scope.glitch.offset = offset_range.min\n",
    "                scope.glitch.width += width_range.step\n",
    "\n",
    "    # we're done\n",
    "    if GLITCH_RESULTS_FILEPATH is not None:\n",
    "        f.close()\n",
    "\n",
    "    # optionally save traces to a file for later processing\n",
    "    if TRACES_FILEPATH is not None:\n",
    "        import numpy as np\n",
    "        trace_array = np.asarray(traces)\n",
    "        print()\n",
    "        print('Saving traces to {}'.format(TRACES_FILEPATH))\n",
    "        np.save(TRACES_FILEPATH, trace_array)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Attacking the 9th round"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### R9: Collecting faulty outputs\n",
    "\n",
    "In this attack, we'll try to glitch the 9th round:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# for scope.glitch.width:\n",
    "if PLATFORM == \"CWLITEXMEGA\" or PLATFORM == \"CW303\":\n",
    "    width_range = Range(7*MIN_STEP, 8*MIN_STEP, MIN_STEP)\n",
    "    offset_range = Range(0.4, 1.4, MIN_STEP)\n",
    "    extoffset_range = Range(12800, 14100, 5)\n",
    "    glitch_repeat = 5\n",
    "elif PLATFORM == \"CWLITEARM\" or PLATFORM == \"CW308_STM32F3\":\n",
    "    width_range = Range(-10, -10.5, MIN_STEP)\n",
    "    offset_range = Range(-39.84, -40.5, MIN_STEP)\n",
    "    extoffset_range = Range(5400, 6700, 3)\n",
    "    glitch_repeat = 3\n",
    "\n",
    "# Example when applying an oscilloscope probe, its capacitance is cushioning glitches so we need to beef them\n",
    "#width_range = Range(9*MIN_STEP, 10*MIN_STEP, MIN_STEP)\n",
    "\n",
    "# for scope.glitch.offset:\n",
    "\n",
    "\n",
    "# for scope.glitch.ext_offset:\n",
    "\n",
    "\n",
    "# for scope.glitch.repeat:\n",
    "\n",
    "\n",
    "apply_ranges()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Yes, I know, `width_range` contains a single value in our setup, but at least you're ready for scanning more values!\n",
    "\n",
    "The next cell runs the glitches campain. Till you don't disconnect the Chipwhisperer, you can re-run the campain and analyze its results several times.  \n",
    "Even when the parameters are perfectly maintained, the glitch effects are never twice exactly the same and the results of our campains may vary quite a lot.  \n",
    "Adjust these parameters if you don't get proper results. Roughly:\n",
    "* Increase `scope.glitch.width` width and/or vary `scope.glitch.offset` if the output is never faulted\n",
    "* Decrease `scope.glitch.width` width and/or vary `scope.glitch.offset` if there is no output (target crashed)\n",
    "* Play also with `scope.glitch.width_fine` if needed\n",
    "* Avoid increasing too much `scope.glitch.repeat` as you don't want to inject mutiple faults affecting several bytes at once\n",
    "* Beware of the effect of an oscilloscope probe if you're monitoring the glitches\n",
    "\n",
    "The goal is to collect as many *interesting* outputs as possible. An *interesting* output at this stage is simply a 16-byte output different from the reference output.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "campaign()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see the results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from terminaltables import AsciiTable\n",
    "table = AsciiTable(results)\n",
    "print(table.table)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### R9: Cryptanalysis of the faulty outputs\n",
    "\n",
    "We'll use `phoenixAES` to perform the DFA against the collected ciphertexts.\n",
    "\n",
    "All it requires is the list of *interesting* outputs and the reference output."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import phoenixAES\n",
    "r10=phoenixAES.crack_bytes(outputs, goldciph, encrypt=True, verbose=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this first attack, we assume the fault was injected *between* the last two *MixColumn* operations and we look for ciphertexts only partially (25%) corrupted.  \n",
    "We hope you managed to recover the full 10th round key. If not, you may try again [from here](#R9:-Collecting-faulty-outputs) :) If you got very few or no \"interesting\" ciphertexts, better to tune `width_range`.   \n",
    "Once the last round key is recovered, you can revert the AES keyscheduling and reveal the actual AES key."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "r9_key=None\n",
    "if r10 is not None:\n",
    "    from chipwhisperer.analyzer.attacks.models.aes.key_schedule import key_schedule_rounds\n",
    "    r9_key = key_schedule_rounds(bytearray.fromhex(r10), 10, 0)\n",
    "    print(\"AES Key:\")\n",
    "    print(''.join(\"%02x\" % x for x in r9_key))\n",
    "else:\n",
    "    print(\"Sorry, no key found, try another campain, maybe with different parameters...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### R9: Plotting inner states differences"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once the AES key is known, we can display where the actual faults were injected, here plotting the first 10 outputs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run \"Helper_Scripts/AES_differential_plotter.ipynb\"\n",
    "curve = None\n",
    "if r9_key is not None:\n",
    "    ad=AesDiff(intext=text, key=r9_key, encrypt=True)\n",
    "    for c in outputs[:10]:\n",
    "        ad.add_glitch(c)\n",
    "    curve=ad.plot_diff_bits()\n",
    "curve.opts(width=600, height=600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This graph shows how many bits were flipped at each round. Of course the plot only makes sense from the lowest points of the curve to the right, there is no fault diffusion from the fault to the left.  \n",
    "We're more interested in the number of bytes which are faulted before the last *MixColumn*:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "curve=None\n",
    "if r9_key is not None:\n",
    "    curve=ad.plot_diff_bytes()\n",
    "    \n",
    "curve.opts(width=600, height=600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We managed to break the key because indeed a number of executions was properly faulted on a single byte before the last *MixColumn*, cf the lowest curves at position 8."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Attacking the 8th round\n",
    "\n",
    "To reduce the number of required faults, we can inject glitches one round earlier.\n",
    "If the faults are injected one *MixColumn* earlier, in the 8th round, the ciphertext will be completely corrupted.\n",
    "But we can still apply the same cryptanalysis!\n",
    "The trick is to convert one such fault into four faults on the 9th round."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### R8: Collecting faulty outputs\n",
    "\n",
    "Let's change our parameters to attack one round earlier and launch our attack."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# for scope.glitch.ext_offset:\n",
    "if PLATFORM == \"CWLITEXMEGA\" or PLATFORM == \"CW303\":\n",
    "    extoffset_range = Range(11800, 12400, 3)\n",
    "elif PLATFORM == \"CWLITEARM\" or PLATFORM == \"CW308_STM32F3\":\n",
    "    extoffset_range = Range(4700, 5400, 3)\n",
    "apply_ranges()\n",
    "campaign()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see the results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from terminaltables import AsciiTable\n",
    "table = AsciiTable(results)\n",
    "print(table.table)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### R8: Cryptanalysis of the faulty outputs\n",
    "\n",
    "In this second attack, we assume the fault was injected *before* the last two *MixColumn* operations.\n",
    "First, we convert each 100% faulty output into four 25% faulty outputs and then we apply the same attack as before."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "outputs2=phoenixAES.convert_r8faults_bytes(outputs, goldciph, encrypt=True)\n",
    "r10=phoenixAES.crack_bytes(outputs2, goldciph, encrypt=True, verbose=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We hope you managed to recover the full 10th round key. If not, you may try again [from here](#R8:-Collecting-faulty-outputs). \n",
    "Once the last round key is recovered, you can revert the AES keyscheduling and reveal the actual AES key."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if r10 is not None:\n",
    "    from chipwhisperer.analyzer.attacks.models.aes.key_schedule import key_schedule_rounds\n",
    "    key = key_schedule_rounds(bytearray.fromhex(r10), 10, 0)\n",
    "    print(\"AES Key:\")\n",
    "    print(''.join(\"%02x\" % x for x in key))\n",
    "else:\n",
    "    print(\"Sorry, no key found, try another campain, maybe with different parameters...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### R8: Plotting inner states differences"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's plot the fault diffusion of the first 10 outputs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run \"Helper_Scripts/AES_differential_plotter.ipynb\"\n",
    "curve=None\n",
    "if key is not None:\n",
    "    ad=AesDiff(intext=text, key=key, encrypt=True)\n",
    "    for c in outputs[:10]:\n",
    "        ad.add_glitch(c)\n",
    "    curve=ad.plot_diff_bits()\n",
    "curve.opts(width=600, height=600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And grouped by faulty bytes. We now see that we get single byte faults one round earlier:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "curve = None\n",
    "if key is not None:\n",
    "    curve=ad.plot_diff_bytes()\n",
    "    \n",
    "curve.opts(width=600, height=600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once you're done, clean up the connection to the scope and target.  \n",
    "**Warning**, once disconnected, you'll have to run the cells since the [CW-lite connection and target flashing](#CW-lite-connection-and-target-flashing) section to be connected again."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.dis()\n",
    "target.dis()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This tutorial is over.  \n",
    "You might now try to attack other instances by yourself, e.g. recompile the target with `CRYPTO_TARGET = \"TINYAES128C\"` and try to break it!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tests"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert key == [43, 126, 21, 22, 40, 174, 210, 166, 171, 247, 21, 136, 9, 207, 79, 60], \"Failed to break r8\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert r9_key == [43, 126, 21, 22, 40, 174, 210, 166, 171, 247, 21, 136, 9, 207, 79, 60], \"Failed to break r9\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
